#include "lists.h"

/** @fn     struct item *startList()
    @brief  starts a wordlist
    @return returns the pointer of the first item in the list
    */
struct item *startList(){
    item *parent;
    item *child;

    //check whether parent and child are succesfully malloced
    if(!(parent = malloc(sizeof(item))) || !(child = malloc(sizeof(item)))){
        if(DEBUG){printf("\nlist init fail");}
        return((item *) NULL);
    }

    //establish relationships between parent and child
    parent->parent = (item *) NULL;
    parent->child = child;
    parent->next = (item *) NULL;
    parent->prev = (item *) NULL;
    parent->count = 0;
    parent->len = 0;
    parent->content = (char *) NULL;


    //set default values
    child->parent = parent;
    child->child = (item *) NULL;
    child->next = (item *) NULL;
    child->prev = (item *) NULL;
    child->count = 0;
    child->len = 0;
    child->content = (char *) NULL;

    if(DEBUG){printf("\nlist init @ 0x%X -> 0x%X", (int) parent, (int) child);}

    return(child);
}

/** @fn     struct item *findLast(item *wRoot)
    @brief  finds the last item in the linked list
    @param  wRoot       pointer to linked list
    @return pointer to the last found item or NULL
    */
struct item *findLast(item *wRoot){
    if(wRoot == (item*) NULL){if(DEBUG > 3){printf("\ninvalid pointer to findLast()");}return((item*) NULL);}

    item *wTemp;
    wTemp = wRoot;

    while(wTemp->next !=NULL){
        wTemp = wTemp->next;
    }
    if(DEBUG > 3){printf("\nlast item @ 0x%X", (int) wTemp);}

    return(wTemp);
}

/** @fn     struct item *findPrev(item *wItem)
    @brief  returns the previous item in the linked list
    @param  wItem       pointer to reference item
    @return struct on success, NULL on fail
    */
struct item *findPrev(item *wItem){
    item *wCurrent;
    if(wItem->prev){
        wCurrent = wItem->prev;
        return(wCurrent);
    }else{
        return(NULL);
    }
}

/** @fn     struct item *findNext(item *wItem)
    @brief  returns the next item in the linked list
    @param  wItem       pointer to reference item
    @return struct on success, NULL on fail
    */
struct item *findNext(item *wItem){
    item *wCurrent;
    if(wItem->next){
        wCurrent = wItem->next;
        return(wCurrent);
    }else{
        return(NULL);
    }
}

/** @fn     struct item *findRoot(item *wItem)
    @brief  finds the root of a linked list
    @param  wItem       item of linked list
    @return pointer to the root or NULL
    */
struct item *findRoot(item *wItem){
    if(wItem == (item*) NULL){if(DEBUG > 3){printf("\ninvalid pointer to findRoot()");}return((item*) NULL);}

    item *wTemp;
    wTemp = wItem;

    while(wTemp->prev !=NULL){
        wTemp = wTemp->prev;
    }

    if(DEBUG > 2){printf("\nfound root @ 0x%X", (int) wTemp);}

    return(wTemp);
}

/** @fn     struct item *findFree(item *wRoot)
    @brief  finds the next free item the linked list
    @param  wCurrent    pointer to current item
    @return pointer of the first vacant item or NULL
    */
struct item *findFree(item *wRoot){
    if(wRoot == (item*) NULL){if(DEBUG > 2){printf("\ninvalid pointer to findFree()");}return((item*) NULL);}
    if(DEBUG>3){printf("\nfinding free of 0x%X", (int) wRoot);}
    item *wCurrent;
    wCurrent = wRoot;

    //scroll through list untill the end of the list or empty item
    while(wCurrent->content != (char *) NULL && wCurrent->next != (item *) NULL){
        wCurrent = wCurrent->next;
    }

    //return address of vacant item upon occurence
    if(wCurrent->content == NULL){
        if(DEBUG > 2){printf("\nfree item found @ 0x%X", (int) wCurrent);}
        return(wCurrent);
    }else{
        if(DEBUG > 3){printf("\nno free items found");}
        return(NULL);
    }
}

/** @fn     int killList(item *wRoot)
    @brief  terminates a constructed list
    @param  wRoot pointer to the list
    @return 2 on items + root terminated, 1 on items terminated, NULL on fail
    */
int killList(item *wRoot){
    if(!wRoot){return(0);}
    item *lastItem;
    lastItem = findLast(wRoot);

    item *wTemp = lastItem;
    //int i = 0;
    if(DEBUG){printf("\nkilling list");}
    while(wTemp != wRoot){
        if(DEBUG == 2){printf("\nKILL:\t\t%X", (int) wTemp);}
        free(wTemp->content);
        free(wTemp);
        wTemp = wTemp->prev;
    }
    if(DEBUG){printf("...oke");}

    free(wRoot->content);

    return(1);
}

/** @fn     int itemCopyTo(item *wSrc, item *wDest)
    @brief  copies the contents of one item into another item
    @param  wSrc    pointer to source item
    @param  wDest   pointer to destination item
    @return 1 on success, NULL on fail
    */
int itemCopyTo(item *wSrc, item *wDest){
    if(!wSrc || !wDest){return((int) NULL);}
    wDest->content = wSrc->content;
    wDest->count = wSrc->count;
    wDest->len = wSrc->len;
    wDest->next = wSrc->next;
    wDest->parent = wSrc->parent;
    wDest->child = wSrc->child;
    wDest->prev = wSrc->prev;
    return(1);
}

/** @fn     int itemSwap(item *wA, item *wB)
    @brief  swaps the contents of two items
    @param  wA      pointer to first item
    @param  wB      pointer to second item
    @return 1 on succes, NULL on fail
    */
int itemSwap(item *wA, item *wB){

    if(wA == NULL || wB == NULL){return((int) NULL);}

    int pWordA = (int) wA->content;
    int lenA = wA->len;
    int countA = wA->count;

    int pWordB = (int) wB->content;
    int lenB = wB->len;
    int countB = wB->count;

    wA->content = (char *) pWordB;
    wB->content = (char *) pWordA;
    wA->len = lenB;
    wB->len = lenA;
    wA->count = countB;
    wB->count = countA;

    return(1);
}

/** @fn     int itemRemove(item *wA)
    @brief  removes an item from  the linked list
    @param  wItem   pointer of the item to remove
    @return returns 1 on succes, 0 on fail;
    */
int itemRemove(item *wItem){
    if(!wItem){return((int) NULL);}

    int next = (int) wItem->next;
    int prev = (int) wItem->prev;

    if(next){wItem->prev->next = (item*) next;}
    if(prev){wItem->next->prev = (item*) prev;}

    free(wItem->content);
    free(wItem);
    return(1);
}

/** @fn     struct item *itemNew(item *wCurrent)
    @brief  creates a new item to the end of a list
    @param  wRoot   pointer to list
    @return returns the new item pointer
    */
struct item *itemNew(item *wRoot){
    item *wItem;
    wItem = findLast(wRoot);

    //if item is actually the last item
    if(wItem->next == NULL){
        //reserve memory for new item
        if((wItem->next = malloc(sizeof(item))) != NULL){
            if(DEBUG>2){printf("\nnew item @ 0x%X...ok", (int) wItem->next);}
            //set the next and previous properties
            wItem->next->prev = (item*) wItem;
            //set new item default properties
            wItem->next->content = NULL;
            wItem->next->count = (int) NULL;
            wItem->next->len = (int) NULL;
            wItem->next->next = (int) NULL;
            wItem->next->parent = wItem->parent;
            return(wItem->next);
        }else{
            //malloc failed
            if(DEBUG>3){printf("\nnew item...memory error");}
            return((item *) NULL);
        }
    }else{
        //not at end of list
        if(DEBUG>3){printf("\nnew item...fail\ncannot add item unless at en of list");}
        return(NULL);
    }
}

/** @fn     void itemSetString(item *wItem, char *string)
    @brief  sets the content and length parameters of a item when given a string
    @param  wItem   the pointer to the item that needs to be set
    @param  string  the pointer to the string that need to be written
    @return void
    */
int itemSetString(item *wItem, char *string){
    //abort upon invalid parameters
    if(wItem == (item *) NULL || string == (char *) NULL){if(DEBUG > 2){printf("\ninvalid setstring pars");return 0;}}

    int len = 0;

    wItem->content = string;

    if(DEBUG>3){printf("\n&con:\t0x%X\ncon:\t0x%X\n*con:\t%s\nstr:\t0x%X\n*str:\t%s", (int) &(wItem->content), (int) (wItem->content), wItem->content, (int) string, string);}

    while(*string != '\0'){
        len++;
        string++;
    }
    wItem->len = len;
    wItem->count++;
    return 1;
}

/** @fn     int itemPopulate(char *string, item *wRoot)
    @brief  adds a string to the list by either creating a new item or populating a emtpy item
    @param  string  the content to be placed into the item
    @param  wRoot   the pointer of the list
    @return 2 on populating new item, 1 on populating existing item, NULL on no populate
    */
int itemPopulate(char *string, item *wRoot){
    //attempt to find a free item in existing list
    item *wFree = findFree(wRoot);

    //populate vacant item if found
    if(wFree != ((item *) NULL)){
        if(itemSetString(wFree, string)){
            if(DEBUG>1){printf("\npopulating @ 0x%X...ok", (int) wFree);}
            return(1);
        }else{
            if(DEBUG>1){printf("\npopulating @ 0x%X...fail", (int) wFree);}
            return(0);
        }
    }
    //else create new item in list
    else{
        wFree = itemNew(wRoot);
        //if creation succeeded
        if(wFree != NULL){
            if(itemSetString(wFree, string)){
                if(DEBUG>1){printf("\npopulating new @ 0x%X...ok", (int) wFree);}
                return(2);
            }else{
                if(DEBUG>1){printf("\npopulating new @ 0x%X...fail", (int) wFree);}
                return(0);
            }

        }
        //if creation of new item failed
        else{
            if(DEBUG>1){printf("\npopulating...fail");}
            return(0);
        }
    }
    return 0;
}

/** @fn     void displayItem(item *wCurrent)
    @brief  displays the contents of a listitem
    @param  wCurrent    the pointer of the item to be displayed
    @return void
    */
void displayItem(item *wCurrent){
    printf("\n\nITEM\t\taddr:\t %X\n\t\tparent:\t %X\n\t\tprev:\t %X\n\t\tnext:\t %X\n\t\tcount:\t %d\n\t\tlen:\t %d\n\t\tcontent: %X -> ",
        (int) wCurrent,
        (int) wCurrent->parent,
        (int) wCurrent->prev,
        (int) wCurrent->next,
        (int) wCurrent->count,
        (int) wCurrent->len,
        (int) wCurrent->content);

    //step through malloc-ed memory space to read the stored string
    char *string = wCurrent->content;
    printf("\"%s\"", string);
    /*int i;
    for(i = 0; i < wCurrent->len; i++){
        //printf("%c", *(string+i));
    }*/
}

/** @fn     struct item *listPrint(item *wRoot)
    @brief  prints an entire linked list
    @param  wRoot       the root of the list
    @return void
    */
void listPrint(item *wRoot){
    //abort upon empty item
    if(!wRoot){if(DEBUG>3){printf("\ninvalid listPrint input");}return;}

    //display root
    displayItem(wRoot);


    //set temp item
    item *wCurrent = wRoot;

    do{
        //loop through additional elements, if any
        displayItem(wCurrent);
        wCurrent = wCurrent->next;
    }while(wCurrent != (item *) NULL);
}

/** @fn     int listCount(item *wRoot)
    @brief  counts the number of items in a list
    @param  wRoot       the root of the list
    @return the amount of records found
    */
int listCount(item *wRoot){
    if(!wRoot){return((int) NULL);}
    item *wTemp = wRoot;
    int i = 1;
    while(wTemp->next){
        i++;
        wTemp = wTemp->next;
    }
    return(i);
}

/** @fn     void listWrite(item *wRoot){
    @brief  writes a list to a file
    @param  wRoot       the root of the list
    @param  fTarget     the pointer to the file
    @return 1 on succes, NULL on fail
    */
void listWrite(item *wRoot, FILE *fTarget){
    int i = 1;

    //abort upon empty item
    if(!wRoot){return;}

    fprintf(fTarget, "index,word,count");

    //set temp item
    item *wCurrent = wRoot;

    do{
        fprintf(fTarget, "\n%d,%s,%d", i, wCurrent->content, wCurrent->count);
        wCurrent = wCurrent->next;
        i++;
    }while(wCurrent != NULL);
}

void printItemCount(item *wCurrent){
    printf("%d", wCurrent->count);
}
void printItemLen(item *wCurrent){
    printf("%d", wCurrent->len);
}
void printItemContent(item *wCurrent){
    char *string = wCurrent->content;
    int i;
    for(i = 0; i < wCurrent->len; i++){
        printf("%c", *(string+i));
    }
}
